NIO概述
Java NIO: Non-blocking IO(非阻塞IO)是jdk 1.4提出的,简单的理解就是使用nio api读写数据的时候不会等待把所有数据读写完再返回(所以读写的时候应该使用while循环不断尝试读写数据)。NIO中关键的三个概念是通道,缓冲区,选择器。
Channel(通道)
所有的数据读写在NIO 中都从Channel 开始,可以把Channel想象成io 中的Stream。数据是从Channel读到Buffer中,或者从Buffer 写到Channel中。Channel主要有以下几种
- FileChannel: 从FileInputStream和FileOutputStream打开的文件通道
- DatagramChannel:通过UDP协议读写网络中的数据
- SocketChannel:通过TCP协议读写网络中的数据
- ServerSocketChannel:对每一个连接创建一个SocketChannel
Buffer(缓冲区)
读取数据的第一步是Channel,第二步是Buffer。数据从通道读取到Buffer中,然后再使用,或者从Buffer中写入通道。Buffer本质上是一块可以写入数据,然后可以从中读取数据的内存,这块内存被包装成NIO Buffer对象。
Buffer主要由三个属性,它们决定了每次读写缓冲区的什么位置。
- capacity:缓冲区的大小
- position:读模式的时候,position为下一个可以读取的位置;写模式的时候,position为下一个可以写入数据的位置
- limit:写模式的时候,可以写入的数据的大小;读模式下,limit表示可以读取的数据大小
当向buffer写入数据时,buffer会记录下写了多少数据。一旦要读取数据,需要通过flip()方法将Buffer从写模式切换到读模式(相应的position和limit值会改变)。在读模式下,可以读取之前写入到buffer的所有数据。
一旦读完Buffer中的数据,需要让Buffer准备好再次被写入。可以通过clear()或compact()方法来完成。clear()将Buffer中剩余的数据清除,compact()会将没有读取的数据移动到Buffer起始处,之后还可以读取。
Buffer.allocate() 直接在Head上分配内存,Buffer.directAllocate()在堆外分配内存(分配的内存不受GC影响,需要自己手动释放)。
Selector(选择器)
Selector(选择器)是NIO中能够侦听多个NIO Channel,并能够知晓这些Channel是否为特定的事件做好准备的组件。
Selector的使用主要是通过如下这种形式来使用
Selector必须和Channel配合使用,而且Channel必须处于非阻塞模式下。通过resigter()方法绑定selector和channel的时候,第二个参数指定了当前channel对什么事件感兴趣。一个channel可以有如下4种感兴趣的事件
- SelectionKey.OP_CONNECT
- SelectionKey.OP_ACCEPT
- SelectionKey.OP_READ
- SelectionKey.OP_WRITE
当channel向selector注册了事件之后,selector.selector()方法会阻塞直到channel关心的哪些事件已经就绪。事件就绪之后判断事件的类别,根据不同类别分别做不同的读写处理。
序列化
一个对象可以被表示为一个字节序列,该字节序列包括该对象的数据、有关对象的类型的信息和存储在对象中数据的类型。
对应的流是ObjectOutputStream,ObjectInputStream。static变量, transient不会序列化,static final会序列化。实现这两个接口的类可以序列化:Serializible ,Externalizible。
如果想自己控制一部分序列化的过程,可以在需要序列化的类种加入private级别的readObject()和writeObject()方法,这两个方法再ObjectInputStream和ObjectOutputStream中会反射调用。write和read的顺序必须一致。使用实现Externalizible接口来序列化,序列化的过程完全可控。
如果需要序列化的对象使用了Signleton模式,(序列化前的对象 == 序列化后的对象)=false,如果要使这两者相等,可以在Singleton类种加入readResolve()方法,在这个方法中返回Signleton对象替换掉在流中创建的对象。——这些都是在同一个jvm中才有用